# -*- coding: utf-8 -*-
"""
预约管理控制器

包含预约系统的核心业务逻辑：
- 预约创建和验证
- 时间冲突检查
- 预约查询和筛选
- 预约状态管理
- 预约统计分析
"""

from datetime import datetime, date, time, timedelta
from decimal import Decimal
from sqlalchemy import and_, or_, extract, func
from flask_login import current_user

from app import db
from app.models.reservation import Reservation
from app.models.user import User
from app.models.table import Table
from app.models.campus import Campus
from app.models.relation import StudentCoachRelation
from app.services.auto_finance_service import AutoFinanceService
from app.controllers.notification_controller import NotificationController
from app.utils.exceptions import BusinessError


class ReservationController:
    """预约管理控制器"""

    @staticmethod
    def create_reservation(student_id, coach_id, table_id, reservation_date, start_time, end_time, price=None, auto_payment=True):
        """
        创建预约

        Args:
            student_id: 学员ID
            coach_id: 教练ID
            table_id: 球台ID
            reservation_date: 预约日期
            start_time: 开始时间
            end_time: 结束时间
            price: 课程费用
            auto_payment: 是否自动扣费

        Returns:
            dict: 操作结果
        """
        try:
            # 1. 验证用户关系
            relation_check = ReservationController._check_student_coach_relation(student_id, coach_id)
            if not relation_check['success']:
                return relation_check

            # 2. 验证时间有效性
            time_check = ReservationController._validate_reservation_time(reservation_date, start_time, end_time)
            if not time_check['success']:
                return time_check

            # 3. 检查时间冲突
            conflict_check = ReservationController._check_time_conflict(table_id, reservation_date, start_time, end_time)
            if not conflict_check['success']:
                return conflict_check

            # 4. 验证球台可用性
            table_check = ReservationController._check_table_availability(table_id, reservation_date, start_time, end_time)
            if not table_check['success']:
                return table_check

            # 5. 计算费用
            if price is None:
                price_result = ReservationController._calculate_price(coach_id, start_time, end_time)
                if not price_result['success']:
                    return price_result
                price = price_result['data']['price']

            # 6. 验证账户余额（如果需要自动扣费）
            if auto_payment:
                balance_validation = AutoFinanceService.validate_balance_for_reservation(
                    student_id, Decimal(str(price))
                )
                if not balance_validation['success']:
                    return {
                        'success': False,
                        'message': balance_validation['error'],
                        'error_code': balance_validation['error_code'],
                        'balance_info': balance_validation
                    }

            # 7. 获取校区信息
            student = User.query.get(student_id)
            if not student:
                return {'success': False, 'message': '学员不存在'}

            # 8. 创建预约
            reservation = Reservation(
                campus_id=student.campus_id,
                student_id=student_id,
                coach_id=coach_id,
                table_id=table_id,
                reservation_date=reservation_date,
                start_time=start_time,
                end_time=end_time,
                price=Decimal(str(price)),
                status='pending'
            )

            db.session.add(reservation)
            db.session.flush()  # 获取预约ID

            # 9. 处理自动扣费
            payment_result = None
            if auto_payment:
                try:
                    payment_result = AutoFinanceService.process_reservation_payment(reservation.id)
                    if payment_result['success']:
                        reservation.status = 'confirmed'
                    else:
                        # 如果扣费失败，保持pending状态，让用户手动处理
                        pass
                except Exception as e:
                    # 如果扣费失败，保持pending状态
                    pass

            db.session.commit()

            # 发送预约通知
            try:
                # 获取学员和教练信息
                student = User.query.get(student_id)
                coach = User.query.get(coach_id)
                table = Table.query.get(table_id)

                if reservation.status == 'confirmed':
                    # 预约已确认，发送确认通知
                    NotificationController.send_reservation_confirmation(
                        reservation.id,
                        student.id,
                        coach.id,
                        {
                            'student_name': student.real_name,
                            'coach_name': coach.real_name,
                            'table_name': table.name if table else f"球台{table_id}",
                            'date': reservation.reservation_date.strftime('%Y年%m月%d日'),
                            'time': f"{reservation.start_time.strftime('%H:%M')}-{reservation.end_time.strftime('%H:%M')}",
                            'price': float(reservation.price),
                            'payment_status': '已支付' if auto_payment else '未支付'
                        }
                    )
                else:
                    # 预约创建但待确认，发送创建通知
                    NotificationController.send_notification(
                        user_id=student.id,
                        title="预约申请已提交",
                        content=f"您的预约申请已成功提交：{reservation.reservation_date.strftime('%Y年%m月%d日')} {reservation.start_time.strftime('%H:%M')}-{reservation.end_time.strftime('%H:%M')}，教练：{coach.real_name}。等待确认中...",
                        notification_type='reservation',
                        related_id=reservation.id
                    )

                    # 通知教练有新的预约申请
                    NotificationController.send_notification(
                        user_id=coach.id,
                        title="收到新的预约申请",
                        content=f"学员{student.real_name}申请预约：{reservation.reservation_date.strftime('%Y年%m月%d日')} {reservation.start_time.strftime('%H:%M')}-{reservation.end_time.strftime('%H:%M')}。请及时处理。",
                        notification_type='reservation',
                        related_id=reservation.id
                    )

            except Exception as e:
                # 通知发送失败不应影响预约创建，只记录日志
                from flask import current_app
                current_app.logger.error(f"发送预约通知失败: {str(e)}")

            result = {
                'success': True,
                'message': '预约创建成功',
                'data': {
                    'reservation': reservation,
                    'reservation_id': reservation.id,
                    'price': float(price),
                    'auto_payment': auto_payment
                }
            }

            # 添加支付信息
            if auto_payment and payment_result:
                result['data']['payment_result'] = payment_result
                if payment_result['success']:
                    result['message'] = '预约创建成功，费用已自动扣除'
                else:
                    result['message'] = f"预约创建成功，但扣费失败：{payment_result.get('error', '未知错误')}"

            return result

        except Exception as e:
            db.session.rollback()
            return {
                'success': False,
                'message': f'创建预约失败：{str(e)}'
            }

    @staticmethod
    def get_student_reservations(student_id, status=None, start_date=None, end_date=None, page=1, per_page=20):
        """
        获取学员预约列表

        Args:
            student_id: 学员ID
            status: 状态筛选
            start_date: 开始日期
            end_date: 结束日期
            page: 页码
            per_page: 每页记录数

        Returns:
            dict: 查询结果
        """
        try:
            query = Reservation.query.filter(Reservation.student_id == student_id)

            # 状态筛选
            if status:
                query = query.filter(Reservation.status == status)

            # 日期范围筛选
            if start_date:
                query = query.filter(Reservation.reservation_date >= start_date)
            if end_date:
                query = query.filter(Reservation.reservation_date <= end_date)

            # 按日期倒序排列
            query = query.order_by(Reservation.reservation_date.desc(), Reservation.start_time.desc())

            # 分页
            pagination = query.paginate(page=page, per_page=per_page, error_out=False)

            return {
                'success': True,
                'data': {
                    'reservations': pagination.items,
                    'pagination': {
                        'total': pagination.total,
                        'pages': pagination.pages,
                        'current_page': pagination.page,
                        'per_page': pagination.per_page,
                        'has_prev': pagination.has_prev,
                        'prev_num': pagination.prev_num,
                        'has_next': pagination.has_next,
                        'next_num': pagination.next_num
                    }
                }
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取预约列表失败：{str(e)}'
            }

    @staticmethod
    def get_reservation_detail(reservation_id, user_id=None):
        """
        获取预约详情

        Args:
            reservation_id: 预约ID
            user_id: 用户ID（权限验证）

        Returns:
            dict: 预约详情
        """
        try:
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                return {'success': False, 'message': '预约不存在'}

            # 权限验证
            if user_id and reservation.student_id != user_id and reservation.coach_id != user_id:
                return {'success': False, 'message': '无权限查看此预约'}

            # 检查评价状态
            from app.models.evaluation import Evaluation
            evaluation = Evaluation.query.filter_by(reservation_id=reservation_id).first()

            evaluation_status = {
                'has_evaluation': evaluation is not None,
                'evaluation': evaluation,
                'student_can_evaluate': False,
                'coach_can_evaluate': False,
                'student_can_modify': False,
                'coach_can_modify': False
            }

            if reservation.status == 'completed':
                if evaluation:
                    # 检查是否可以修改评价（24小时内）
                    from datetime import datetime, timedelta
                    if evaluation.student_submitted_at:
                        hours_since_student_eval = (datetime.now() - evaluation.student_submitted_at).total_seconds() / 3600
                        evaluation_status['student_can_modify'] = hours_since_student_eval < 24
                    if evaluation.coach_submitted_at:
                        hours_since_coach_eval = (datetime.now() - evaluation.coach_submitted_at).total_seconds() / 3600
                        evaluation_status['coach_can_modify'] = hours_since_coach_eval < 24
                else:
                    # 没有评价记录，可以创建评价
                    evaluation_status['student_can_evaluate'] = True
                    evaluation_status['coach_can_evaluate'] = True

            return {
                'success': True,
                'data': {
                    'reservation': reservation,
                    'can_edit': reservation.status == 'pending',
                    'can_cancel': reservation.can_be_cancelled(),
                    'time_until_start': ReservationController._get_time_until_start(reservation),
                    'cancellation_deadline': ReservationController._get_cancellation_deadline(reservation),
                    'evaluation_status': evaluation_status
                }
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取预约详情失败：{str(e)}'
            }

    @staticmethod
    def update_reservation(reservation_id, user_id, **kwargs):
        """
        更新预约信息

        Args:
            reservation_id: 预约ID
            user_id: 操作用户ID
            **kwargs: 更新字段

        Returns:
            dict: 操作结果
        """
        try:
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                return {'success': False, 'message': '预约不存在'}

            # 权限验证
            if reservation.student_id != user_id:
                return {'success': False, 'message': '无权限修改此预约'}

            # 状态验证
            if reservation.status != 'pending':
                return {'success': False, 'message': '只能修改待确认的预约'}

            # 时间限制验证
            if not reservation.can_be_cancelled():
                return {'success': False, 'message': '预约开始前24小时内不能修改'}

            # 验证修改字段
            allowed_fields = ['reservation_date', 'start_time', 'end_time', 'table_id']
            update_data = {k: v for k, v in kwargs.items() if k in allowed_fields}

            if not update_data:
                return {'success': False, 'message': '没有可更新的字段'}

            # 如果修改时间相关字段，需要重新验证
            if any(field in update_data for field in ['reservation_date', 'start_time', 'end_time', 'table_id']):
                new_date = update_data.get('reservation_date', reservation.reservation_date)
                new_start = update_data.get('start_time', reservation.start_time)
                new_end = update_data.get('end_time', reservation.end_time)
                new_table_id = update_data.get('table_id', reservation.table_id)

                # 时间有效性验证
                time_check = ReservationController._validate_reservation_time(new_date, new_start, new_end)
                if not time_check['success']:
                    return time_check

                # 冲突检查
                conflict_check = ReservationController._check_time_conflict(
                    new_table_id, new_date, new_start, new_end, exclude_id=reservation_id
                )
                if not conflict_check['success']:
                    return conflict_check

            # 执行更新
            for field, value in update_data.items():
                setattr(reservation, field, value)

            # 如果修改了时间，重新计算时长
            if 'start_time' in update_data or 'end_time' in update_data:
                reservation.calculate_duration()

            reservation.updated_at = datetime.now()
            db.session.commit()

            return {
                'success': True,
                'message': '预约修改成功',
                'data': {'reservation': reservation}
            }

        except Exception as e:
            db.session.rollback()
            return {
                'success': False,
                'message': f'修改预约失败：{str(e)}'
            }

    @staticmethod
    def cancel_reservation(reservation_id, user_id, reason=None):
        """
        取消预约（集成自动退款）

        Args:
            reservation_id: 预约ID
            user_id: 取消用户ID
            reason: 取消原因

        Returns:
            dict: 操作结果
        """
        try:
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                return {'success': False, 'message': '预约不存在'}

            # 权限验证
            if reservation.student_id != user_id and reservation.coach_id != user_id:
                return {'success': False, 'message': '无权限取消此预约'}

            # 取消验证
            if not reservation.can_be_cancelled():
                return {'success': False, 'message': '预约不能取消'}

            # 检查取消次数限制
            cancel_limit_check = ReservationController._check_cancel_limit(user_id)
            if not cancel_limit_check['success']:
                return cancel_limit_check

            # 处理自动退款
            refund_result = None
            try:
                refund_result = AutoFinanceService.process_reservation_refund(
                    reservation_id, reason or '用户取消预约'
                )
            except Exception as e:
                # 退款失败不应阻止取消，但需要记录
                refund_result = {
                    'success': False,
                    'error': str(e),
                    'refund_amount': 0
                }

            # 执行取消
            success = reservation.cancel(user_id, reason)
            if not success:
                return {'success': False, 'message': '取消预约失败'}

            db.session.commit()

            # 构建返回结果
            result = {
                'success': True,
                'message': '预约取消成功',
                'data': {
                    'reservation': reservation,
                    'refund_result': refund_result
                }
            }

            # 根据退款结果调整消息
            if refund_result:
                if refund_result['success']:
                    if refund_result.get('no_refund_needed'):
                        result['message'] = '预约取消成功（无需退款）'
                    elif refund_result.get('no_refund_due_to_policy'):
                        result['message'] = f"预约取消成功，{refund_result.get('message', '根据取消政策不予退款')}"
                    else:
                        refund_amount = refund_result.get('refund_amount', 0)
                        penalty_amount = refund_result.get('penalty_amount', 0)
                        if penalty_amount > 0:
                            result['message'] = f'预约取消成功，退款{refund_amount}元（扣除违约金{penalty_amount}元）'
                        else:
                            result['message'] = f'预约取消成功，已退款{refund_amount}元'
                else:
                    result['message'] = f"预约取消成功，但退款失败：{refund_result.get('error', '未知错误')}"

            return result

        except Exception as e:
            db.session.rollback()
            return {
                'success': False,
                'message': f'取消预约失败：{str(e)}'
            }

    @staticmethod
    def get_available_coaches(student_id, campus_id=None):
        """
        获取学员可选教练列表

        Args:
            student_id: 学员ID
            campus_id: 校区ID

        Returns:
            dict: 教练列表
        """
        try:
            # 获取有师生关系的教练
            coach_relations = StudentCoachRelation.query.filter(
                StudentCoachRelation.student_id == student_id,
                StudentCoachRelation.status == 'approved'
            ).all()

            coach_ids = [relation.coach_id for relation in coach_relations]

            if not coach_ids:
                return {
                    'success': True,
                    'data': {'coaches': []},
                    'message': '暂无可选教练，请先申请师生关系'
                }

            # 获取教练详细信息
            query = User.query.filter(
                User.id.in_(coach_ids),
                User.status == 'active'
            )

            if campus_id:
                query = query.filter(User.campus_id == campus_id)

            coaches = query.all()

            return {
                'success': True,
                'data': {'coaches': coaches}
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取教练列表失败：{str(e)}'
            }

    @staticmethod
    def get_available_tables(campus_id, reservation_date, start_time, end_time):
        """
        获取可用球台列表

        Args:
            campus_id: 校区ID
            reservation_date: 预约日期
            start_time: 开始时间
            end_time: 结束时间

        Returns:
            dict: 可用球台列表
        """
        try:
            # 获取校区所有可用球台
            all_tables = Table.query.filter(
                Table.campus_id == campus_id,
                Table.status == 'available'
            ).all()

            available_tables = []

            for table in all_tables:
                # 检查时间冲突
                conflict = Reservation.check_time_conflict(
                    table.id, reservation_date, start_time, end_time
                )
                if not conflict:
                    available_tables.append(table)

            return {
                'success': True,
                'data': {'tables': available_tables}
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取可用球台失败：{str(e)}'
            }

    @staticmethod
    def get_calendar_data(student_id, year=None, month=None):
        """
        获取日历视图数据

        Args:
            student_id: 学员ID
            year: 年份
            month: 月份

        Returns:
            dict: 日历数据
        """
        try:
            if not year:
                year = datetime.now().year
            if not month:
                month = datetime.now().month

            # 计算月份范围
            start_date = date(year, month, 1)
            if month == 12:
                end_date = date(year + 1, 1, 1) - timedelta(days=1)
            else:
                end_date = date(year, month + 1, 1) - timedelta(days=1)

            # 获取月份内所有预约，预加载相关数据
            from sqlalchemy.orm import joinedload
            reservations = Reservation.query.options(
                joinedload(Reservation.coach),
                joinedload(Reservation.table)
            ).filter(
                Reservation.student_id == student_id,
                Reservation.reservation_date >= start_date,
                Reservation.reservation_date <= end_date
            ).order_by(Reservation.reservation_date, Reservation.start_time).all()

            # 按日期分组，转换为JSON可序列化的格式
            calendar_data = {}
            for reservation in reservations:
                date_str = reservation.reservation_date.isoformat()
                if date_str not in calendar_data:
                    calendar_data[date_str] = []

                # 转换为字典格式，确保包含所有必需的数据
                reservation_data = {
                    'id': reservation.id,
                    'start_time': reservation.start_time.strftime('%H:%M'),
                    'end_time': reservation.end_time.strftime('%H:%M'),
                    'status': reservation.status,
                    'price': float(reservation.price),
                    'coach': {
                        'id': reservation.coach.id,
                        'real_name': reservation.coach.real_name or reservation.coach.username
                    } if reservation.coach else {
                        'id': reservation.coach_id,
                        'real_name': '未知教练'
                    },
                    'table': {
                        'id': reservation.table.id,
                        'name': reservation.table.name
                    } if reservation.table else {
                        'id': reservation.table_id,
                        'name': f'球台{reservation.table_id}'
                    }
                }

                calendar_data[date_str].append(reservation_data)

            return {
                'success': True,
                'data': {
                    'calendar_data': calendar_data,
                    'year': year,
                    'month': month,
                    'start_date': start_date,
                    'end_date': end_date
                }
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取日历数据失败：{str(e)}'
            }

    @staticmethod
    def get_student_statistics(student_id):
        """
        获取学员预约统计

        Args:
            student_id: 学员ID

        Returns:
            dict: 统计数据
        """
        try:
            now = datetime.now()
            current_month_start = date(now.year, now.month, 1)

            # 基础统计
            total_reservations = Reservation.query.filter(
                Reservation.student_id == student_id
            ).count()

            completed_reservations = Reservation.query.filter(
                Reservation.student_id == student_id,
                Reservation.status == 'completed'
            ).count()

            cancelled_reservations = Reservation.query.filter(
                Reservation.student_id == student_id,
                Reservation.status == 'cancelled'
            ).count()

            # 本月统计
            month_reservations = Reservation.query.filter(
                Reservation.student_id == student_id,
                Reservation.reservation_date >= current_month_start
            ).count()

            upcoming_reservations = Reservation.query.filter(
                Reservation.student_id == student_id,
                Reservation.status.in_(['pending', 'confirmed']),
                Reservation.reservation_date >= date.today()
            ).count()

            return {
                'success': True,
                'data': {
                    'total_reservations': total_reservations,
                    'completed_reservations': completed_reservations,
                    'cancelled_reservations': cancelled_reservations,
                    'month_reservations': month_reservations,
                    'upcoming_reservations': upcoming_reservations,
                    'completion_rate': round(completed_reservations / total_reservations * 100, 1) if total_reservations > 0 else 0,
                    'cancellation_rate': round(cancelled_reservations / total_reservations * 100, 1) if total_reservations > 0 else 0
                }
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'获取统计数据失败：{str(e)}'
            }

    # 私有辅助方法

    @staticmethod
    def _check_student_coach_relation(student_id, coach_id):
        """检查师生关系"""
        relation = StudentCoachRelation.query.filter(
            StudentCoachRelation.student_id == student_id,
            StudentCoachRelation.coach_id == coach_id,
            StudentCoachRelation.status == 'approved'
        ).first()

        if not relation:
            return {
                'success': False,
                'message': '您与该教练暂无师生关系，无法预约课程'
            }

        return {'success': True}

    @staticmethod
    def _validate_reservation_time(reservation_date, start_time, end_time):
        """验证预约时间"""
        # 检查日期不能是过去
        if reservation_date < date.today():
            return {'success': False, 'message': '不能预约过去的日期'}

        # 检查开始时间小于结束时间
        if start_time >= end_time:
            return {'success': False, 'message': '开始时间必须早于结束时间'}

        # 检查时长限制（至少30分钟，最多4小时）
        start_dt = datetime.combine(reservation_date, start_time)
        end_dt = datetime.combine(reservation_date, end_time)
        duration_minutes = (end_dt - start_dt).seconds / 60

        if duration_minutes < 30:
            return {'success': False, 'message': '预约时长不能少于30分钟'}
        if duration_minutes > 240:
            return {'success': False, 'message': '预约时长不能超过4小时'}

        # 检查预约时间是否太远（最多提前30天）
        max_date = date.today() + timedelta(days=30)
        if reservation_date > max_date:
            return {'success': False, 'message': '最多只能提前30天预约'}

        # 检查是否提前足够时间预约（至少提前2小时）
        reservation_datetime = datetime.combine(reservation_date, start_time)
        min_advance_time = datetime.now() + timedelta(hours=2)
        if reservation_datetime < min_advance_time:
            return {'success': False, 'message': '至少需要提前2小时预约'}

        return {'success': True}

    @staticmethod
    def _check_time_conflict(table_id, reservation_date, start_time, end_time, exclude_id=None):
        """检查时间冲突"""
        has_conflict = Reservation.check_time_conflict(
            table_id, reservation_date, start_time, end_time, exclude_id
        )

        if has_conflict:
            return {
                'success': False,
                'message': '该时间段球台已被预约，请选择其他时间'
            }

        return {'success': True}

    @staticmethod
    def _check_table_availability(table_id, reservation_date, start_time, end_time):
        """检查球台可用性"""
        table = Table.query.get(table_id)
        if not table:
            return {'success': False, 'message': '球台不存在'}

        if table.status != 'available':
            return {'success': False, 'message': '球台当前不可用'}

        # 可以添加更多球台状态检查逻辑
        return {'success': True}

    @staticmethod
    def _calculate_price(coach_id, start_time, end_time):
        """计算课程费用"""
        try:
            coach = User.query.get(coach_id)
            if not coach:
                return {'success': False, 'message': '教练不存在'}

            # 计算时长（小时）
            start_dt = datetime.combine(date.today(), start_time)
            end_dt = datetime.combine(date.today(), end_time)
            duration_hours = (end_dt - start_dt).seconds / 3600

            # 获取教练课时费（假设有一个基础费率）
            hourly_rate = getattr(coach, 'hourly_rate', 100)  # 默认100元/小时

            total_price = float(hourly_rate) * duration_hours

            return {
                'success': True,
                'data': {
                    'price': total_price,
                    'hourly_rate': hourly_rate,
                    'duration_hours': duration_hours
                }
            }

        except Exception as e:
            return {
                'success': False,
                'message': f'计算费用失败：{str(e)}'
            }

    @staticmethod
    def _check_cancel_limit(user_id):
        """检查取消次数限制"""
        now = datetime.now()
        year_month = f"{now.year}-{now.month:02d}"

        cancel_count = Reservation.get_user_monthly_cancellations(user_id, year_month)

        # 每月最多取消3次
        if cancel_count >= 3:
            return {
                'success': False,
                'message': '本月取消次数已达上限（3次），无法继续取消预约'
            }

        return {'success': True}

    @staticmethod
    def _get_time_until_start(reservation):
        """获取距离开始时间"""
        start_datetime = reservation.get_datetime_start()
        now = datetime.now()

        if start_datetime > now:
            delta = start_datetime - now
            hours = int(delta.total_seconds() / 3600)
            return f"{hours}小时后开始"
        else:
            return "已开始"

    @staticmethod
    def _get_cancellation_deadline(reservation):
        """获取取消截止时间"""
        start_datetime = reservation.get_datetime_start()
        deadline = start_datetime - timedelta(hours=24)
        return deadline